/**
* \file DGVDiophantineVTK.cpp
*
* \brief This file defines a class for drawing lines on lattices.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DGVDiophantineVTK.h"

#include <QApplication>

#include "DGVFile.h"

DGVDiophantineVTK::DGVDiophantineVTK(QWidget *parent) : QVTKWidget(parent)
{
    latticeSet = false;
    generatedBefore = false;
    manual = false;

    pointOnLine.resize(3);
    pointOnLine = 0;

    intercept = 0;

    displayProperties = vtkProperty::New();
}

DGVDiophantineVTK::~DGVDiophantineVTK()
{
    if(generatedBefore)
    {
        line->Delete();
        mapper->Delete();
        actor->Delete();
    }
    displayProperties->Delete();
}

void DGVDiophantineVTK::setLattice(DGVLatticeVTK *lattice)
{
    gamma = lattice;

    start.resize(gamma->getDimension());
    start = 0.0;
    end.resize(gamma->getDimension());
    end = 1.0;

    latticeSet = true;
}

void DGVDiophantineVTK::setCoefficients(DiophantineType x, DiophantineType y, DiophantineType z)
{
    xCoefficient = x;
    yCoefficient = y;
    zCoefficient = z;
    manual = false;
}

void DGVDiophantineVTK::setPointOnLine(DiophantineType x, DiophantineType y, DiophantineType z)
{
    pointOnLine = x, y, z;

    ///c = by - ax
    intercept = yCoefficient*pointOnLine(1) - xCoefficient*pointOnLine(0);
    cerr << "Intercept: " << intercept << endl;
    cerr << "WARNING: 3 Dimensions not supported for lines yet!" << endl;

}

void DGVDiophantineVTK::setStartPoint(Array<DiophantineType,1> &point)
{
    if(latticeSet)
    {
        start = 1.0*point;
        manual = true;
    }
}

void DGVDiophantineVTK::setStartPoint(DiophantineType x, DiophantineType y)
{
    if(latticeSet)
    {
        if(gamma->getDimension() == 2)
        {
            start(0) = x;
            start(1) = y;
            manual = true;
        }
    }
}

void DGVDiophantineVTK::setEndPoint(Array<DiophantineType,1> &point)
{
    if(latticeSet)
    {
        end = 1.0*point;
        manual = true;
    }
}

void DGVDiophantineVTK::setEndPoint(DiophantineType u, DiophantineType v)
{
    if(latticeSet)
    {
        if(gamma->getDimension() == 2)
        {
            end(0) = u;
            end(1) = v;
            manual = true;
        }
    }
}

void DGVDiophantineVTK::setColour(double red, double green, double blue)
{
    displayProperties->SetColor(red,green,blue);
}

DGVLatticeVTK* DGVDiophantineVTK::loadFile(QString filename)
{
    ///Read settings file
	int xSize_Primary = 32, ySize_Primary = 32, xSize_Secondary = 0, ySize_Secondary = 0, red = 0, green = 0, blue = 0;
	Array<long,1> integersRead;
	DGVLatticeVTK *lattice = new DGVLatticeVTK;
	DGVFile inFile;

	inFile.open(filename,"r");

    ///Ignore Comments
    qApp->processEvents();
	inFile.readNextLineAsIntegers(integersRead);
	while(integersRead.size() == 0) //Ignore comments
        inFile.readNextLineAsIntegers(integersRead);

    ///Read Primary Lattice
    if(integersRead.size() != 5)
    {
        cerr << ">| File FORMAT ERROR: Check file format at Primary Lattice!" << endl;
        return lattice;
    }

    xSize_Primary = integersRead(0);
    ySize_Primary = integersRead(1);
    red = integersRead(2);
    green = integersRead(3);
    blue = integersRead(4);
    cerr << ">| Primary: " << xSize_Primary << "x" << ySize_Primary << endl;

    gamma = lattice;
	lattice->setLatticeSize(xSize_Primary,ySize_Primary);
	lattice->setLatticeColour(red,green,blue);
	lattice->generateLattice();
	lattice->viewAsMatrix();
	lattice->enableAxes();
	lattice->resize(minWindowSize,minWindowSize);
	latticeSet = true;

	///Ignore Comments
    inFile.readNextLineAsIntegers(integersRead);
	while(integersRead.size() == 0) //Ignore comments
        inFile.readNextLineAsIntegers(integersRead);

    ///Read Secondary Lattice
    if(integersRead.size() != 5)
    {
        cerr << ">| File FORMAT ERROR: Check file format at Secondary Lattice!" << endl;
        return lattice;
    }
    xSize_Secondary = integersRead(0);
    ySize_Secondary = integersRead(1);
    red = integersRead(2);
    green = integersRead(3);
    blue = integersRead(4);
    cerr << ">| Secondary: " << xSize_Secondary << "x" << ySize_Secondary << endl;

    DiophantineType xStart = xSize_Primary/2 - xSize_Secondary/2, yStart = ySize_Primary/2 - ySize_Secondary/2;
	for(int j = xStart; j < xSize_Secondary+xStart; j ++)
        for(int k = yStart; k < ySize_Secondary+xStart; k ++)
        {
            qApp->processEvents();
            lattice->setLatticePointColour(j,k,red,green,blue);
        }

    ///Ignore Comments
    inFile.readNextLineAsIntegers(integersRead);
	while(integersRead.size() == 0) //Ignore comments
        inFile.readNextLineAsIntegers(integersRead);

    ///Read Background Colour
    if(integersRead.size() != 3)
    {
        cerr << ">| File FORMAT ERROR: Check file format at Background Colour!" << endl;
        return lattice;
    }
    red = integersRead(0);
    green = integersRead(1);
    blue = integersRead(2);
    cerr << ">| Background Colour: " << red << ", " << green << ", " << blue << endl;
    if(red >= 0 && blue >= 0 && green >= 0)
        lattice->setBackgroundColour(red,green,blue);

	//--------------------------------------------
	///Display lines
	Array<DGVDiophantineVTK*,1> lines;
	int noOfLines = 0, noOfPoints = 0;
	DiophantineType p = 0, q = 0, x = 0, y = 0;

    while(!inFile.atEnd())
    {
        qApp->processEvents();

        ///Ignore Comments
        inFile.readNextLineAsIntegers(integersRead);
        while(integersRead.size() == 0 && !inFile.atEnd()) //Ignore comments
            inFile.readNextLineAsIntegers(integersRead);

        ///Read Lines and Points
        if(integersRead.size() == 3) //Colour
        {
            red = integersRead(0);
            green = integersRead(1);
            blue = integersRead(2);
        }
        else if(integersRead.size() == 4) //Vector
        {
            x = integersRead(0)+xStart;
            y = integersRead(1)+yStart;
            q = integersRead(2)+xStart;
            p = integersRead(3)+yStart;
            lines.resizeAndPreserve(noOfLines+1);
            lines(noOfLines) = new DGVDiophantineVTK;
            lines(noOfLines)->setLattice(lattice);
            lines(noOfLines)->setStartPoint(x,y);
            lines(noOfLines)->setEndPoint(integersRead(0)+q,integersRead(1)+p);
            lines(noOfLines)->setColour(red,green,blue);
            lines(noOfLines)->generateLine();
            noOfLines ++;
        }
        else if(integersRead.size() == 5) //Point
        {
            x = integersRead(0)+xStart;
            y = integersRead(1)+yStart;
            red = integersRead(2);
            green = integersRead(3);
            blue = integersRead(4);
            lattice->setLatticePointColour(x,y,red,green,blue);
            noOfPoints ++;
        }
        else if(integersRead.size() == 7) //Line
        {
            x = integersRead(0)+xStart;
            y = integersRead(1)+yStart;
            red = integersRead(2);
            green = integersRead(3);
            blue = integersRead(4);
            p = integersRead(5);
            q = integersRead(6);
            lines.resizeAndPreserve(noOfLines+1);
            lines(noOfLines) = new DGVDiophantineVTK;
            lines(noOfLines)->setLattice(lattice);
            lines(noOfLines)->setCoefficients(p,q,0);
            lines(noOfLines)->setPointOnLine(x,y,0);
            lines(noOfLines)->setColour(red,green,blue);
            lines(noOfLines)->generateLine();
            noOfLines ++;
        }
        else if(integersRead.size() == 0)
            continue;
        else
        {
            cerr << ">| File FORMAT ERROR: Check file format at Line Parameters!" << endl;
            cerr << ">| No more points/lines will be done!" << endl;
            break;
        }
    }

    cerr << ">| No. Of Lines: " << noOfLines << ", No. of Points: " << noOfPoints << endl;
    return lattice;
}

void DGVDiophantineVTK::generateLine()
{
    if(latticeSet)
    {
        Array<latticeType,1> parameters(gamma->getLatticeParameters());
        double p1_x, p1_y;


        if(!generatedBefore)
        {
            line = vtkLineSource::New();
            mapper = vtkPolyDataMapper::New();
            actor = vtkActor::New();
            generatedBefore = true;
        }

        parameters = gamma->getLatticeParameters();
        if(!manual)
        {
            ///Determine bounds for the line within the lattice
            Array<int,1> extents(gamma->getDimension());

            extents = gamma->getLatticeSize();
            if(gamma->getDimension() == 2)
            {
                p1_x = 0;
                p1_y = static_cast<double>(xCoefficient)/yCoefficient*p1_x + static_cast<double>(intercept)/yCoefficient;
                if(p1_y >= 0 && p1_y < extents(1))
                {
                    start(0) = p1_x;
                    start(1) = p1_y;
                }
                p1_x = extents(0)-1;
                p1_y = static_cast<double>(xCoefficient)/yCoefficient*p1_x + static_cast<double>(intercept)/yCoefficient;
                if(p1_y >= 0 && p1_y < extents(1))
                {
                    end(0) = p1_x;
                    end(1) = p1_y;
                }
                p1_y = 0;
                p1_x = static_cast<double>(yCoefficient)/xCoefficient*p1_y - static_cast<double>(intercept)/xCoefficient;
                if(p1_x >= 0 && p1_x < extents(0))
                {
                    start(0) = p1_x;
                    start(1) = p1_y;
                }
                p1_y = extents(1)-1;
                p1_x = static_cast<double>(yCoefficient)/xCoefficient*p1_y - static_cast<double>(intercept)/xCoefficient;
                if(p1_x >= 0 && p1_x < extents(0))
                {
                    end(0) = p1_x;
                    end(1) = p1_y;
                }
            }
            else
            {
                cerr << "ERROR: Dimension not supported for lines yet!" << endl;
                return;
            }
        }

        line->SetPoint1(start(0)*parameters(0),start(1)*parameters(1),0); ///For vectors, start and end already set
        line->SetPoint2(end(0)*parameters(0),end(1)*parameters(1),0);
        line->Update();

        mapper->SetInput(line->GetOutput());
        //mapper->SetScalarRange(0, radius);
        mapper->Update();

        actor->SetMapper(mapper);
        actor->SetProperty(displayProperties);
        gamma->addActorToLattice(actor);
    }
}
